//
//  ARDataCenter.swift
//  AquaRaft
//
//  Created by 何启亮 on 2024/4/7.
//

import Foundation
import Cache

/// 本地数据的处理中心
/// - note:
///  1. 首页作品数据
///  2. 视频流数据
///  都以userId为表名
class ARDataCenter {
    
    // MARK: - Private property
    
    /// 作品: key为workTableKey
    private var workStorage: Storage<String, [String: ARWorkModel]>?
    private static let workStorageName = "AR_dataCenter_workStorage_"
    
    /// 推荐的作品列表
    private var recommendStorage: Storage<String, [String: ARWorkModel]>?
    private static let recommendStorageName = "AR_dataCenter_recommendStorage_"
    
    /// 作者列表: key为authorTableKey
    private var authorStorage: Storage<String, [String: ARUserModel]>?
    private static let authorStorageName = "AR_dataCenter_authorStorage_"
    
    /// 视频作品: key为videoWorkTableKey
    private var videoWorkStorage: Storage<String, [String: ARVideoWorkModel]>?
    private static let videoWorkStorageName = "AR_dataCenter_videoWorkStorage_"
    
    /// 用户关系表 - 关注/拉黑
    private var relationshipStorage: Storage<String, ARUserRelationShipModel>?
    private static let relationshipStorageName = "AR_dataCenter_relationshipStorage_"
    
    /// 礼物
    private var giftStorage: Storage<String, [String: ARGiftRecorderModel]>?
    private static let giftStorageName = "AR_dataCenter_giftStorage_"
    
    /// 作品表
    private static let workTableKey = "ar_work_table"
    /// 作者表
    private static let authorTableKey = "ar_author_table"
    /// 推荐地点
    private static let recommendTableKey = "ar_recommend_table"
    /// 视频作品
    private static let videoWorkTableKey = "ar_videoWork_table"
    /// 用户关系表名
    private static let userRelationshipTableKey = "ar_userRelationship_table"
    /// 礼物
    private static let giftTableKey = "ar_gift_table"
    
    /// 主要使用这两个表
    private var workDict: [String: ARWorkModel] = [:]
    private var authorDict: [String: ARUserModel] = [:]
    private var recommendationDict: [String: ARWorkModel] = [:]
    private var videoWorkDict: [String: ARVideoWorkModel] = [:]
    
    /// 当前用户的用户关系表
    private var userRelationshipModel: ARUserRelationShipModel = ARUserRelationShipModel()
    
    private var giftDict: [String: ARGiftRecorderModel] = [:]
    
    // MARK: - 单例
    
    static let `default` = ARDataCenter()
    
    private init() {
        configCenter()
    }
    
    private func configCenter() {
        NotificationCenter.default.addObserver(self, selector: #selector(loginStatusDidChangeNotificationHandler(_:)), name: .userLoginStateChage, object: nil)
    }
    
    deinit {
        NotificationCenter.default.removeObserver(self)
    }
    
    // MARK: - Notification
    
    @objc private func loginStatusDidChangeNotificationHandler(_ noti: Notification) {
        guard let userId = ARUserManager.shared.userId else {
            // 清除数据
            workStorage = nil
            authorStorage = nil
            return
        }
        
        self.authorDict.removeAll()
        self.workDict.removeAll()
        self.recommendationDict.removeAll()
        self.videoWorkDict.removeAll()
        self.userRelationshipModel = ARUserRelationShipModel()// 重置
        self.giftDict.removeAll()
        
        // 普通作品
        workStorage = try? Storage(diskConfig: DiskConfig(name: "\(Self.workStorageName)\(userId)"), memoryConfig: MemoryConfig(), transformer: TransformerFactory.forCodable(ofType: [String: ARWorkModel].self))
        // 作者
        authorStorage = try? Storage(diskConfig: DiskConfig(name: "\(Self.authorStorageName)\(userId)"), memoryConfig: MemoryConfig(), transformer: TransformerFactory.forCodable(ofType: [String: ARUserModel].self))
        // 地点
        recommendStorage = try? Storage(diskConfig: DiskConfig(name: "\(Self.recommendStorageName)\(userId)"), memoryConfig: MemoryConfig(), transformer: TransformerFactory.forCodable(ofType: [String: ARWorkModel].self))
        // 视频
        videoWorkStorage = try? Storage(diskConfig: DiskConfig(name: "\(Self.videoWorkStorageName)\(userId)"), memoryConfig: MemoryConfig(), transformer: TransformerFactory.forCodable(ofType: [String: ARVideoWorkModel].self))
        // 用户关系
        relationshipStorage = try? Storage(diskConfig: DiskConfig(name: Self.relationshipStorageName+userId), memoryConfig: MemoryConfig(), transformer: TransformerFactory.forCodable(ofType: ARUserRelationShipModel.self))
        // 礼物
        giftStorage = try? Storage(diskConfig: DiskConfig(name: "\(Self.giftStorageName)\(userId)"), memoryConfig: MemoryConfig(), transformer: TransformerFactory.forCodable(ofType: [String: ARGiftRecorderModel].self))
        
        if let workDict = try? workStorage?.object(forKey: Self.workTableKey) {
            self.workDict.merge(workDict, uniquingKeysWith: { (current, _) in current})
        }
        if let authorDict = try? authorStorage?.object(forKey: Self.authorTableKey) {
            self.authorDict.merge(authorDict, uniquingKeysWith: { (current, _) in current})
        }
        if let recommendationDict = try? recommendStorage?.object(forKey: Self.recommendTableKey) {
            self.recommendationDict.merge(recommendationDict, uniquingKeysWith: { (current, _) in current})
        }
        if let videoWorkDict = try? videoWorkStorage?.object(forKey: Self.videoWorkTableKey) {
            self.videoWorkDict.merge(videoWorkDict, uniquingKeysWith: { (current, _) in current})
        }
        if let relationshipModel = try? relationshipStorage?.object(forKey: Self.userRelationshipTableKey) {
            self.userRelationshipModel = relationshipModel
        }
        if let giftDict = try? giftStorage?.object(forKey: Self.giftTableKey) {
            self.giftDict = giftDict
        }
    }
    
}

// MARK: - 作品

/// 获取作品
extension ARDataCenter {
    
    /// 获取作品
    func fetchWorkList(_ completion: @escaping ([ARWorkModel]?, ARDataCenterError?) -> Void) {
        guard let workStorage = workStorage else {
            completion(nil, .permissionDenied)
            return
        }
        let allObjects = self.workDict.allValues()
        if allObjects.isEmpty == false {
            // 不为空 - 要获取作者
            var targetWork: [ARWorkModel] = []
            for work in allObjects {
                do {
                    try self.configWork(work)
                    if work.author?.isBlock ?? false {
                        // 拉黑
                        continue
                    }
                    targetWork.append(work)
                } catch {
                    // Do nothing...
                }
            }
            completion(targetWork, nil)
            return
        }
        // 为空 -> 第一次 -> 生成作品
        self.fetchUserList { [weak self] userList, err in
            guard let self = self else {
                completion(nil, .unknown)
                return
            }
            
            if let err = err {
                completion(nil, err)
                return
            }
            
            guard let data = Data.ReferenceType(contentsOfFile: Bundle.main.path(forResource: "works.plist", ofType: nil)!), let userList = userList else {
                completion(nil, .unknown)
                return
            }
            
            // 用户获取成功
            let works: [[ARWorkModel]]
            do {
                let decoder = PropertyListDecoder()
                works = try decoder.decode([[ARWorkModel]].self, from: data as Data)
            } catch {
                completion(nil, .storageError(err: error))
                return
            }
            
            // 对works插入作者
            var targetWork: [ARWorkModel] = []
            for (index, workList) in works.enumerated() {
                let userIndex = index % userList.count
                
                let user = userList[userIndex]
                for work in workList {
                    
                    work.authorId = user.userId
                    work.author = user
                    
                    work.likeNum = Int.random(in: 10...30)
                    self.workDict[work.id] = work
                    
                    targetWork.append(work)
                }
            }
            
            // 记录
            do {
                try workStorage.setObject(self.workDict, forKey: Self.workTableKey)
            } catch {
                debugPrint("workStorge error: \(error)")
            }
            
            completion(targetWork, nil)
        }
    }
    
    func fetchWork(_ workId: String) -> ARWorkModel? {
        if  let work = self.workDict[workId] {
            try? self.configWork(work)
            return work
        }
        return nil
    }
    
    /// 点赞、取消点赞
    func likeOrUnlikeWork(_ workId: String, like: Bool, completion: @escaping (_ err: ARDataCenterError?) -> Void) {
        
        guard let workStorage = workStorage else {
            completion(.permissionDenied)
            return
        }
        
        guard let work = self.workDict[workId] else {
            completion(.modelNotFound)
            return
        }
        
        work.isLike = like
        var likeNum = work.likeNum ?? 0
        if like {
            likeNum += 1
        } else {
            likeNum -= 1
        }
        if likeNum < 0 {
            likeNum = 0
        }
        work.likeNum = likeNum
        
        do {
            try workStorage.setObject(self.workDict, forKey: Self.workTableKey)
            completion(nil)
        } catch {
            completion(.storageError(err: error))
        }
        
        // 发送通知
        NotificationCenter.default.post(name: .workLikeChange, object: nil, userInfo: [
            "work": work,
        ])
    }
    
    /// 发布地点 - 首页进入的 - 需要underReview
    func postWork(_ work: ARWorkModel, completion: @escaping (ARDataCenterError?) -> Void) {
        guard let workStorage = workStorage else {
            completion(.permissionDenied)
            return
        }
        
        self.workDict[work.id] = work
        // 记录
        do {
            try workStorage.setObject(self.workDict, forKey: Self.workTableKey)
            ARHelper.afterAsyncInMainQueue {
                completion(nil)
            }
        } catch {
            completion(.storageError(err: error))
        }
    }
    
    /// 获取某个用户的所有作品
    func fetchUserAllWorkList(_ userId: String, completion: @escaping ([ARWorkModel]?, ARDataCenterError?) -> Void) {
        let allObjects = self.workDict.allValues()
        if allObjects.isEmpty == false {
            // 不为空 - 要获取作者
            var targetWork: [ARWorkModel] = []
            for work in allObjects {
                guard let authorId = work.authorId,
                      authorId == userId else {
                    continue
                }
                do {
                    try self.configWork(work)
                    targetWork.append(work)
                } catch {
                    // Do nothing...
                }
            }
            completion(targetWork, nil)
            return
        }
        
        // 空
        completion(allObjects, nil)
    }
    
    func deleteWork(_ workId: String, completion: @escaping (ARDataCenterError?) -> Void) {
        guard let work = self.fetchWork(workId) else {
            completion(.modelNotFound)
            return
        }
        
        // 判断用户
        guard let workStorage = workStorage,
              let userId = ARUserManager.shared.userId,
              let authorId = work.authorId,
              userId == authorId else {
            completion(.permissionDenied)
            return
        }
        
        // 相等 - 可以删除
        self.workDict.removeValue(forKey: workId)
        do {
            try workStorage.setObject(self.workDict, forKey: Self.workTableKey)
            completion(nil)
        } catch {
            completion(.storageError(err: error))
        }
    }
    
    // MARK: Comment
    
    func postTextComment(_ comment: String, workId: String, completion: @escaping (_ commentModel: ARCommentModel?, _ err: ARDataCenterError?) -> Void) {
        
        let commentModel = ARCommentModel()
        commentModel.commentType = .text
        commentModel.comment = comment
        self.postComment(commentModel, workId: workId, completion: completion)
    }
    
    func postAudioComment(_ path: String, duration: TimeInterval, workId: String, completion: @escaping (_ commentModel: ARCommentModel?, _ err: ARDataCenterError?) -> Void) {
        let commentModel = ARCommentModel()
        commentModel.commentType = .audio
        commentModel.audioDuration = duration
        commentModel.audioPath = path
        self.postComment(commentModel, workId: workId, completion: completion)
    }
    
    private func postComment(_ comment: ARCommentModel, workId: String, completion: @escaping (_ commentModel: ARCommentModel?, _ err: ARDataCenterError?) -> Void) {
        guard let workStorage = workStorage, let userId = ARUserManager.shared.userId else {
            completion(nil, .permissionDenied)
            return
        }
        
        guard let work = self.fetchWork(workId) else {
            completion(nil, .modelNotFound)
            return
        }
        
        // 设置 id / authorid
        comment.authorId = userId
        comment.id = "commnet_\(userId)_\(Date().timeIntervalSince1970)"
        
        if comment.checkModelValid() == false {
            completion(nil, .unknown)
            return
        }
        
        // 更新
        var comments = work.comments ?? []
        comments.append(comment)
        try? comment.author = self.fetchUser(comment.authorId)
        work.comments = comments
        self.workDict[workId] = work
        do {
            try workStorage.setObject(self.workDict, forKey: Self.workTableKey)
            completion(comment, nil)
        } catch {
            completion(nil, .storageError(err: error))
        }
    }
    
    /// comment点赞、取消点赞
    func likeOrUnlikeComment(_ workId: String, commentId: String ,like: Bool, completion: @escaping (_ err: ARDataCenterError?) -> Void) {
        
        guard let workStorage = workStorage else {
            completion(.permissionDenied)
            return
        }
        
        guard let work = self.workDict[workId],
              let comments = work.comments,
              let commentModel = comments.first(where: { $0.id == commentId}) else {
            completion(.modelNotFound)
            return
        }
        
        commentModel.isLike = like
        
        do {
            try workStorage.setObject(self.workDict, forKey: Self.workTableKey)
            completion(nil)
        } catch {
            completion(.storageError(err: error))
        }
    }
    
    // MARK: Helper
    
    /// 配置work的author、comment - comment直接记录在work上
    private func configWork(_ work: ARWorkModel) throws {
        guard let authorId = work.authorId else {
            throw ARDataCenterError.unknown
        }
        
        do {
            
            work.author = try self.fetchUser(authorId)
            // comment
            if let comments = work.comments {
                for comment in comments {
                    comment.author = try self.fetchUser(comment.authorId)
                }
                // 重新设置一下
                work.comments = comments
            }
        } catch {
            debugPrint("Set up work info error: \(error)")
        }
    }
    
}

// MARK: - Recommend

extension ARDataCenter {

    /// 不在审核中的推荐地点
    func fetchRecommendationList(_ completion: @escaping ([ARWorkModel]?, ARDataCenterError?) -> Void) {
        guard let recommendStorage = recommendStorage else {
            completion(nil, .permissionDenied)
            return
        }
        let allObjects = self.recommendationDict.allValues()
        if allObjects.isEmpty == false {
            // 不为空 - 要获取作者
            var targetWork: [ARWorkModel] = []
            for work in allObjects {
                if work.isReview ?? false {
                    continue
                }
                do {
                    try self.configWork(work)
                    if work.author?.isBlock ?? false {
                        // 拉黑
                        continue
                    }
                    targetWork.append(work)
                } catch {
                    // Do nothing...
                }
            }
            completion(targetWork, nil)
            return
        }
        // 为空 -> 第一次 -> 生成作品
        self.fetchUserList { [weak self] userList, err in
            guard let self = self else {
                completion(nil, .unknown)
                return
            }
            
            if let err = err {
                completion(nil, err)
                return
            }
            
            guard let data = Data.ReferenceType(contentsOfFile: Bundle.main.path(forResource: "recommend.plist", ofType: nil)!), let userList = userList else {
                completion(nil, .unknown)
                return
            }
            
            // 用户获取成功
            let works: [ARWorkModel]
            do {
                let decoder = PropertyListDecoder()
                works = try decoder.decode([ARWorkModel].self, from: data as Data)
            } catch {
                completion(nil, .storageError(err: error))
                return
            }
            
            // 对works插入作者
            var targetWork: [ARWorkModel] = []
            for (index, work) in works.enumerated() {
                let userIndex = index % userList.count
                
                let user = userList[userIndex]
                work.authorId = user.userId
                work.author = user
                
                work.likeNum = Int.random(in: 10...30)
                self.recommendationDict[work.id] = work
                
                targetWork.append(work)
            }
            
            // 记录
            do {
                try recommendStorage.setObject(self.recommendationDict, forKey: Self.recommendTableKey)
            } catch {
                debugPrint("workStorge error: \(error)")
            }
            
            completion(targetWork, nil)
        }
    }
    
    // 审核中的作品不能点赞、comment等操作
    
    /// review 地点
    /// - note: 目前只有登录用户，所以这里是获取登录用户所有发表的作品
    func fetchLoginUserAllReviewRecommendationList(_ completion: @escaping ([ARWorkModel]?, ARDataCenterError?) -> Void) {
        guard let userId = ARUserManager.shared.userId else {
            completion(nil, .permissionDenied)
            return
        }
        let allObjects = self.recommendationDict.allValues()
        if allObjects.isEmpty == false {
            // 不为空 - 要获取作者
            var targetWork: [ARWorkModel] = []
            for work in allObjects {
                if work.authorId ?? "" != userId {
                    continue
                }
                do {
                    try self.configWork(work)
                    targetWork.append(work)
                } catch {
                    // Do nothing...
                }
            }
            completion(targetWork, nil)
            return
        }
        
        // 空
        completion(allObjects, nil)
    }
    
    /// 删除当前登录用户的推荐
    func deleteLoginUserRecommendation(_ workId: String, completion: @escaping (ARDataCenterError?) -> Void) {
        guard let recommendStorage = recommendStorage else {
            completion(.permissionDenied)
            return
        }
        
        self.recommendationDict.removeValue(forKey: workId)
        // 记录
        do {
            try recommendStorage.setObject(self.recommendationDict, forKey: Self.recommendTableKey)
            ARHelper.afterAsyncInMainQueue {
                completion(nil)
            }
        } catch {
            completion(.storageError(err: error))
        }
    }
    
    /// 获取review 地点
    /// - note: 因为只有登录用户才会发表，所以这里不做区分
    func fetchRecommendation(_ workId: String) -> ARWorkModel? {
        if  let work = self.recommendationDict[workId] {
            try? self.configWork(work)
            return work
        }
        return nil
    }
    
    /// 点赞、取消点赞
    func likeOrUnlikeRecommendation(_ workId: String, like: Bool, completion: @escaping (_ err: ARDataCenterError?) -> Void) {
        
        guard let recommendStorage = recommendStorage else {
            completion(.permissionDenied)
            return
        }
        
        guard let work = self.recommendationDict[workId] else {
            completion(.modelNotFound)
            return
        }
        
        work.isLike = like
        var likeNum = work.likeNum ?? 0
        if like {
            likeNum += 1
        } else {
            likeNum -= 1
        }
        if likeNum < 0 {
            likeNum = 0
        }
        work.likeNum = likeNum
        
        do {
            try recommendStorage.setObject(self.recommendationDict, forKey: Self.recommendTableKey)
            completion(nil)
        } catch {
            completion(.storageError(err: error))
        }
    }
    
    /// 发布地点 - 首页进入的 - 需要underReview
    func postRecommendation(_ work: ARWorkModel, completion: @escaping (ARDataCenterError?) -> Void) {
        guard let recommendStorage = recommendStorage else {
            completion(.permissionDenied)
            return
        }
        
        work.isReview = true
        self.recommendationDict[work.id] = work
        // 记录
        do {
            try recommendStorage.setObject(self.recommendationDict, forKey: Self.recommendTableKey)
            ARHelper.afterAsyncInMainQueue {
                completion(nil)
            }
        } catch {
            completion(.storageError(err: error))
        }
    }
    
    // MARK: Comment
    
    func postRecommendationTextComment(_ comment: String, workId: String, completion: @escaping (_ commentModel: ARCommentModel?, _ err: ARDataCenterError?) -> Void) {
        
        let commentModel = ARCommentModel()
        commentModel.commentType = .text
        commentModel.comment = comment
        self.postRecommendationComment(commentModel, workId: workId, completion: completion)
    }
    
    private func postRecommendationComment(_ comment: ARCommentModel, workId: String, completion: @escaping (_ commentModel: ARCommentModel?, _ err: ARDataCenterError?) -> Void) {
        guard let recommendStorage = recommendStorage, let userId = ARUserManager.shared.userId else {
            completion(nil, .permissionDenied)
            return
        }
        
        guard let work = self.fetchRecommendation(workId) else {
            completion(nil, .modelNotFound)
            return
        }
        
        // 设置 id / authorid
        comment.authorId = userId
        comment.id = "commnet_\(userId)_\(Date().timeIntervalSince1970)"
        
        if comment.checkModelValid() == false {
            completion(nil, .unknown)
            return
        }
        
        // 更新
        var comments = work.comments ?? []
        comments.append(comment)
        try? comment.author = self.fetchUser(comment.authorId)
        work.comments = comments
        self.recommendationDict[workId] = work
        do {
            try recommendStorage.setObject(self.recommendationDict, forKey: Self.recommendTableKey)
            completion(comment, nil)
        } catch {
            completion(nil, .storageError(err: error))
        }
    }
    
    /// comment点赞、取消点赞
    func likeOrUnlikeRecommendationComment(_ workId: String, commentId: String ,like: Bool, completion: @escaping (_ err: ARDataCenterError?) -> Void) {
        
        guard let recommendStorage = recommendStorage else {
            completion(.permissionDenied)
            return
        }
        
        guard let work = self.recommendationDict[workId],
              let comments = work.comments,
              let commentModel = comments.first(where: { $0.id == commentId}) else {
            completion(.modelNotFound)
            return
        }
        
        commentModel.isLike = like
        
        do {
            try recommendStorage.setObject(self.recommendationDict, forKey: Self.recommendTableKey)
            completion(nil)
        } catch {
            completion(.storageError(err: error))
        }
    }
    
}

// MARK: - 用户

/// 获取用户
extension ARDataCenter {
    
    // MARK: Query
    
    func fetchUserList(_ completion: @escaping ([ARUserModel]?, ARDataCenterError?) -> Void) {
        
        guard let authorStorage = authorStorage else {
            completion(nil, .permissionDenied)
            return
        }
        
        // 判断是否已获取 - 因为第一次一定是从网络获取
        let allObjects = self.authorDict.allValues()
        if allObjects.isEmpty == false {
            // 已存在
            completion(allObjects, nil)
            return
        }
        
        // 从网络获取
        ARFetchAuthorRequest().fetchAuthorList { [weak self] userList, err in
            guard let self = self else {
                completion(nil, .permissionDenied)
                return
            }
            if let err = err {
                completion(userList, .apiError(err: err))
                return
            }
            
            if let userList = userList {
                // 保存至storage中
                for user in userList {
                    user.followNum = Int.random(in: 5...20)
                    self.authorDict[user.userId] = user
                }
                
                authorStorage.async.setObject(self.authorDict, forKey: Self.authorTableKey) { result in
                    // Do nothing...
                }
            }
            completion(userList, nil)
        }
    }
    
    func fetchUser(_ userId: String) throws -> ARUserModel  {
        if let currentUserId = ARUserManager.shared.userId, userId == currentUserId {
            if let userModel = ARUserManager.shared.userModel {
                return userModel
            }
            
            throw ARDataCenterError.unknown
        }
        guard let author = self.authorDict[userId] else {
            throw ARDataCenterError.unknown
        }
        
        author.isFollow = self.followOrUnfollowFor(userId)
        author.isBlock = self.isInBlackList(userId)
        
        return author
    }
}

// MARK: - Video work

extension ARDataCenter {
    
    // MARK: Query
    
    /// 获取作品
    func fetchVideoWorkList(_ completion: @escaping ([ARVideoWorkModel]?, ARDataCenterError?) -> Void) {
        guard let videoWorkStorage = videoWorkStorage else {
            completion(nil, .permissionDenied)
            return
        }
        let allObjects = self.videoWorkDict.allValues()
        if allObjects.isEmpty == false {
            // 不为空 - 要获取作者
            var targetWork: [ARVideoWorkModel] = []
            for work in allObjects {
                do {
                    try self.configVideoWork(work)
                    if work.author?.isBlock ?? false {
                        // 拉黑
                        continue
                    }
                    targetWork.append(work)
                } catch {
                    // Do nothing...
                }
            }
            completion(targetWork, nil)
            return
        }
        // 为空 -> 第一次 -> 生成作品
        self.fetchUserList { [weak self] userList, err in
            guard let self = self else {
                completion(nil, .unknown)
                return
            }
            
            if let err = err {
                completion(nil, err)
                return
            }
            
            guard let data = Data.ReferenceType(contentsOfFile: Bundle.main.path(forResource: "videoWorks.plist", ofType: nil)!), let userList = userList else {
                completion(nil, .unknown)
                return
            }
            
            // 用户获取成功
            let works: [ARVideoWorkModel]
            do {
                let decoder = PropertyListDecoder()
                works = try decoder.decode([ARVideoWorkModel].self, from: data as Data)
            } catch {
                completion(nil, .storageError(err: error))
                return
            }
            
            // 对works插入作者
            var targetWork: [ARVideoWorkModel] = []
            for (index, work) in works.enumerated() {
                let userIndex = index % userList.count
                
                let user = userList[userIndex]
                work.authorId = user.userId
                work.author = user
                self.videoWorkDict[work.id] = work
                targetWork.append(work)
            }
            
            // 记录
            do {
                try videoWorkStorage.setObject(self.videoWorkDict, forKey: Self.videoWorkTableKey)
            } catch {
                debugPrint("workStorge error: \(error)")
            }
            
            completion(targetWork, nil)
        }
    }
    
    func fetchVideoWork(_ workId: String) -> ARVideoWorkModel? {
        if  let work = self.videoWorkDict[workId] {
            try? self.configVideoWork(work)
            return work
        }
        return nil
    }
    
    // MARK: Action
    
    /// 点赞、取消点赞
    func likeOrUnlikeVideoWork(_ workId: String, like: Bool, completion: @escaping (_ err: ARDataCenterError?) -> Void) {
        
        guard let videoWorkStorage = videoWorkStorage else {
            completion(.permissionDenied)
            return
        }
        
        guard let videoWork = self.videoWorkDict[workId] else {
            completion(.modelNotFound)
            return
        }
        
        videoWork.isLike = like
        
        do {
            try videoWorkStorage.setObject(self.videoWorkDict, forKey: Self.videoWorkTableKey)
            completion(nil)
        } catch {
            completion(.storageError(err: error))
        }
    }
    
    // MARK: Helper
    
    /// 配置work的author、comment - comment直接记录在work上
    private func configVideoWork(_ work: ARVideoWorkModel) throws {
        guard let authorId = work.authorId else {
            throw ARDataCenterError.unknown
        }
        
        do {
            
            work.author = try self.fetchUser(authorId)
            // comment
            if let comments = work.comments {
                for comment in comments {
                    comment.author = try self.fetchUser(comment.authorId)
                }
                // 重新设置一下
                work.comments = comments
            }
        } catch {
            debugPrint("Set up work info error: \(error)")
        }
    }
}

// MARK: - User relationship

extension ARDataCenter {
    
    // MARK: Query
    
    /// 查询是否有关注
    func followOrUnfollowFor(_ userId: String) -> Bool {
        return self.userRelationshipModel.followOrUnfollow(userId)
    }
    
    func isInBlackList(_ userId: String) -> Bool {
        return self.userRelationshipModel.isInBlackList(userId)
    }
    
    /// 获取某个用户的black list
    func blackListFor(_ userId: String, completion: @escaping ([ARUserModel] , ARDataCenterError?) -> Void) {
        
        var target: [ARUserModel] = []
        if let blackList = self.userRelationshipModel.blackList {
            for userId in blackList {
                if let user = try? self.fetchUser(userId) {
                    target.append(user)
                }
            }
        }
        completion(target, nil)
    }
    
    /// 获取某个用户的follow list
    func followListFor(_ userId: String, completion: @escaping ([ARUserModel] , ARDataCenterError?) -> Void) {
        
        var target: [ARUserModel] = []
        if let followList = self.userRelationshipModel.followList {
            for userId in followList {
                if let user = try? self.fetchUser(userId) {
                    target.append(user)
                }
            }
        }
        completion(target, nil)
    }
    
    // MARK: Action
    
    /// 关注 - 取消关注
    func followOrUnfollowUser(_ userId: String, followOrUnfollow: Bool, completion: @escaping (_ err: ARDataCenterError?) -> Void) {
        ARFollowOrUnfollowRequest().followOrUnfollowUser(userId, followOrUnfollow: followOrUnfollow) { [weak self] err in
            // Do nothing...
        }
        self.__followOrUnfollowUserInLocalDB(userId, followOrUnfollow: followOrUnfollow, completion: completion)
    }
    
    private func __followOrUnfollowUserInLocalDB(_ userId: String, followOrUnfollow: Bool, completion: @escaping (_ err: ARDataCenterError?) -> Void) {
        
        guard let relationshipStorage = relationshipStorage else {
            completion(.permissionDenied)
            return
        }
        
        if followOrUnfollow {
            self.userRelationshipModel.followUser(userId)
        } else {
            self.userRelationshipModel.unfollowUser(userId)
        }
        
        // 刷新一下
        if let user = try? self.fetchUser(userId) {
            user.isFollow = followOrUnfollow
        }
        
        do {
            try relationshipStorage.setObject(self.userRelationshipModel, forKey: Self.userRelationshipTableKey)
            completion(nil)
        } catch {
            completion(.storageError(err: error))
        }
    }
    
    /// 拉黑 - 取消拉黑
    /// - Parameters:
    ///   - addOrRemove: 拉黑或取消拉黑
    ///   - subReason: 如果有值，则默认是举报, addOrRemoveBlackList == false 时无效
    func addOrRemoveBlackList(_ userId: String, addOrRemove: Bool, subReason: String? = nil, completion: @escaping (_ err: ARDataCenterError?) -> Void) {
        ARAddOrRemoveBlackListRequest().addOrRemoveBlackListFor(userId, addOrRemoveBlackList: addOrRemove, subReason: subReason) { [weak self] err in
            // Do nothing...
        }
        self.__addOrRemoveBlackListInLocalDB(userId, addOrRemove: addOrRemove, completion: completion)
    }
    
    private func __addOrRemoveBlackListInLocalDB(_ userId: String, addOrRemove: Bool, completion: @escaping (_ err: ARDataCenterError?) -> Void) {
        
        guard let relationshipStorage = relationshipStorage else {
            completion(.permissionDenied)
            return
        }
        
        if addOrRemove {
            self.userRelationshipModel.addBlackList(userId)
        } else {
            self.userRelationshipModel.removeBlackList(userId)
        }
        
        // 刷新一下
        if let user = try? self.fetchUser(userId) {
            user.isBlock = addOrRemove
        }
        
        do {
            try relationshipStorage.setObject(self.userRelationshipModel, forKey: Self.userRelationshipTableKey)
            completion(nil)
        } catch {
            completion(.storageError(err: error))
        }
        
        // 通知
        NotificationCenter.default.post(name: .blackListChange, object: nil)
    }
    
}

// MARK: - Gift

extension ARDataCenter {
    
    func boatGiftNumOf(_ userId: String) -> Int {
        guard let giftStorage = giftStorage else {
            return 0
        }
        if let model = self.giftDict[userId] {
            return model.boat
        }
        return 0
    }
    
    func waterGunGiftNumOf(_ userId: String) -> Int {
        guard let giftStorage = giftStorage else {
            return 0
        }
        if let model = self.giftDict[userId] {
            return model.waterGun
        }
        return 0
    }
    
    func swimmingRingGiftNumOf(_ userId: String) -> Int {
        guard let giftStorage = giftStorage else {
            return 0
        }
        if let model = self.giftDict[userId] {
            return model.swimmingRing
        }
        return 0
    }
    
    func sendBoatGiftTo(_ userId: String) {
        guard let giftStorage = giftStorage else {
            return
        }
        
        var model = self.giftDict[userId]
        if model == nil {
            model = ARGiftRecorderModel()
        }
        model!.boat += 1
        self.giftDict[userId] = model!
        try? giftStorage.setObject(self.giftDict, forKey: Self.giftTableKey)
    }
    
    func sendWaterGunGiftTo(_ userId: String) {
        guard let giftStorage = giftStorage else {
            return
        }
        
        var model = self.giftDict[userId]
        if model == nil {
            model = ARGiftRecorderModel()
        }
        model!.waterGun += 1
        self.giftDict[userId] = model!
        try? giftStorage.setObject(self.giftDict, forKey: Self.giftTableKey)
    }
    
    func sendSwimmingRingGiftTo(_ userId: String) {
        guard let giftStorage = giftStorage else {
            return
        }
        
        var model = self.giftDict[userId]
        if model == nil {
            model = ARGiftRecorderModel()
        }
        model!.swimmingRing += 1
        self.giftDict[userId] = model!
        try? giftStorage.setObject(self.giftDict, forKey: Self.giftTableKey)
    }
    
}

// MARK: - Define

extension Notification.Name {
    
    static let workLikeChange = Notification.Name.init("ar.workLikeChangeNotification")
    static let blackListChange = Notification.Name.init("ar.blacklistChangeNotification")
    
}

enum ARDataCenterError: Error, LocalizedError {
    
    case apiError(err: Error)
    case storageError(err: Error)
    /// 没有权限(未登录)
    case permissionDenied
    
    /// 查找失败
    case modelNotFound
    
    case unknown
    
    var errorDescription: String? {
        switch self {
        case .apiError(let err):
            return err.localizedDescription
        case .storageError(let err):
            return err.localizedDescription
        case .permissionDenied:
            return "Permission denied"
        case .unknown:
            return "Unknown error"
        case .modelNotFound:
            return "Failed to find"
        }
    }
}

/// 单纯记录关系
class ARUserRelationShipModel: Codable {
    
    // 黑名单
    var blackList: [String]?
    // follow
    var followList: [String]?
    
    // - 查询
    
    func followOrUnfollow(_ userId: String) -> Bool {
        return followList?.contains(where: { $0 == userId}) ?? false
    }
    
    func isInBlackList(_ userId: String) -> Bool {
        return blackList?.contains(where: { $0 == userId}) ?? false
    }
    
    // - 操作
    
    func addBlackList(_ userId: String) {
        var blackList = blackList
        if blackList == nil {
            blackList = []
        }
        // 判断是否存在
        if blackList!.contains(where: { $0 == userId}) {
            return
        }
        blackList!.append(userId)
        self.blackList = blackList
        
        // 同时移除关注
        self.unfollowUser(userId)
    }
    
    func removeBlackList(_ userId: String) {
        self.blackList?.remove(userId)
    }
    
    func followUser(_ userId: String) {
        var followList = followList
        if followList == nil {
            followList = []
        }
        // 判断是否存在
        if followList!.contains(where: { $0 == userId}) {
            return
        }
        followList!.append(userId)
        self.followList = followList
    }
    
    func unfollowUser(_ userId: String) {
        self.followList?.remove(userId)
    }
    
}

class ARGiftRecorderModel: Codable {
    
    var boat: Int
    var waterGun: Int
    var swimmingRing: Int
    
    init(boat: Int = 0, waterGun: Int = 0, swimmingRing: Int = 0) {
        self.boat = boat
        self.waterGun = waterGun
        self.swimmingRing = swimmingRing
    }
}
